
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.DataFormatException;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.ZipInputStream;

/**
 *
 * @author Divine Kota :P
 */
public class OpenerAppMain {

    private static File input;
    private static RandomAccessFile readRaf;
    private static RandomAccessFile writeRaf;
    private static String path;
    private static Vector<FileData> data = new Vector<FileData>();

    public static String getPath() {
        return path;
    }

    public static Vector<FileData> getData() {
        return data;
    }

    public static int readDword(byte[] tab) {
        String str = "";
        for (int j = tab.length - 1; j >= 0; j--) {
            str += Integer.toHexString((int) tab[j] & 0xFF);
        }
        return Integer.parseInt(str, 16);
    }

    public static FileData readDir() throws IOException {
        
        FileData fileData = new FileData();
        //odczytaj długość nazwy
        byte[] dword = new byte[4];
        readRaf.read(dword, 0, dword.length);
        int nml = readDword(dword);
        byte[] name = new byte[nml];
        readRaf.read(name, 0, name.length);
        fileData.setFileName(new String(name));
        //jeśli nie jest to plik pro ani inny potrzebny, przesuń filepointer z rafu do końca info o tym 
        //pliku w dir tree i zwróć null, bo 
        //po cholerę czytać dalej i czas tracić :D, potem nulle nie są dodawane do vectora
        if (!fileData.getFileName().contains("\\ITEMS\\") && !fileData.getFileName().endsWith("STAT.MSG") &&
                !fileData.getFileName().endsWith("pro_item.msg") && !fileData.getFileName().endsWith("PERK.MSG") &&
                !fileData.getFileName().endsWith("PROTO.MSG")) {
            long fp = readRaf.getFilePointer();
            readRaf.seek(fp + 13);
            return null;
        }
        //odczytaj kompresję
        fileData.isCompressed = (readRaf.readByte() == 1 ? true : false);
        //odczytaj realsize
        readRaf.read(dword, 0, dword.length);
        fileData.setFileSize(readDword(dword));
        //odczytaj compressedSize
        readRaf.read(dword, 0, dword.length);
        fileData.setCompressedSize(readDword(dword));
        //odczytaj offset
        readRaf.read(dword, 0, dword.length);
        fileData.setOffset(readDword(dword));

        return fileData;

    }
    //nie mam siły komentować
    //spać :/
    public static void writeFile(FileData fdata) throws IOException {
        readRaf.seek(0);
        readRaf.seek(fdata.getOffset());
        
        byte[] dataa = new byte[fdata.getCompressedSize()];
        readRaf.read(dataa, 0, dataa.length);
        ByteArrayInputStream in = new ByteArrayInputStream(dataa);
        Inflater decompresser = new Inflater();
        
        
        decompresser.setInput(dataa);
        byte[] result = new byte[fdata.getFileSize()];
        int resultLength = 0;
        try {
            resultLength = decompresser.inflate(result);
        } catch (DataFormatException ex) {  
            Logger.getLogger(OpenerAppMain.class.getName()).log(Level.SEVERE, null, ex);
        }
        decompresser.end();
        
        
        File finalfile = null;
        if (fdata.getFileName().endsWith("PRO") || fdata.getFileName().endsWith("pro")) {
            finalfile = new File(path + "\\temp\\items\\" + fdata.getFileName().substring(fdata.getFileName().length()-12, fdata.getFileName().length()));
        } else if (fdata.getFileName().endsWith("lst")) {
            finalfile = new File(path + "\\temp\\items\\items.lst");
        }
        else {
            boolean create = new File(path + "\\" + fdata.getFileName().substring(0, 17)).mkdirs(); 
            //taaak, tu wyżej jest brzydka magic number fuj fuj fuj ale nie chce mi się 
            //tego przerabiać na razie
            finalfile = new File(path + "\\" + fdata.getFileName());
        }
            writeRaf = new RandomAccessFile(finalfile, "rw");
            writeRaf.write(result, 0, resultLength);
        }
        
    

    public static void main(String[] args) throws IOException {
        
        // ścieżynkę trza zmienić ;)
        path = "C:\\programs\\fallout2";
        //master z którego czytamy
        input = new File(path + "\\MASTER.DAT");
        //rozmiar mastera :D:D:D
        long inputSize = input.length();
        
        boolean created = new File(path + "\\temp\\msg").mkdirs();
        created = new File(path + "\\temp\\items").mkdir();

        try {
            readRaf = new RandomAccessFile(input, "rw");

            try {
                //licz offset
                readRaf.seek(inputSize - 8);
                byte[] size = new byte[4];
                readRaf.read(size, 0, size.length);
                int dirTreesize = readDword(size);
                readRaf.read(size, 0, size.length);
                int archiveSize = readDword(size);
                long begOffset = (long) archiveSize - (long) dirTreesize - 4;

                //czytaj treeSize
                //przejdź do offsetu
                readRaf.seek(begOffset);

                while (readRaf.getFilePointer() != inputSize - 8) {
                    FileData newData = readDir();
                    if (!(newData == null)) {
                        data.addElement(newData);
                    }
                }
                //===========TEST==========//
                System.out.println(String.valueOf(data.size()));
                //===========TEST==========//
                
                for (int i = 0; i < data.size(); i++) {
                    writeFile(data.elementAt(i));                    
                }

            } catch (IOException ex) {
                Logger.getLogger(OpenerAppMain.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(OpenerAppMain.class.getName()).log(Level.SEVERE, null, ex);
        }






        //Skasuj tempa na koniec działania JVM. Albo nie kasuj?
        //Na wszelki wypadek, jakby użyszkodnik sobie zażyczył, to opcja będzie dostępna
        //Ta końcówka pójdzie do falloutedytora, ale tu sobie ją napiszę, 
        // bo zapomnę potem jak wepchnę w tamten syf ;)

        (new File(path + "\\temp")).deleteOnExit();














    }
}
